/*==========================================================================
 * 
 * Buffer.java
 * 
 * $Author: randallco $
 * $Revision: 1.3 $
 * $Date: 2006/04/18 01:37:22 $
 * $Name:  $
 * 
 * Created on 27-Jan-2004
 * Created by Marcel Palko alias Randallco (randallco@users.sourceforge.net)
 *==========================================================================*/
package net.sourceforge.ehep.gui;

import org.eclipse.swt.*;
import org.eclipse.core.runtime.IPath;
import org.eclipse.jface.dialogs.MessageDialog;
import org.eclipse.swt.graphics.Cursor;
import org.eclipse.swt.graphics.Color;
import java.io.File;
import java.io.FileWriter;
import java.io.FileReader;
import java.io.BufferedWriter;
import java.io.BufferedReader;
import java.io.IOException;
import java.io.FileNotFoundException;
import java.util.Random;

import net.sourceforge.ehep.EhepPlugin;
import net.sourceforge.ehep.core.*;

/**
 * @author Marcel Palko
 * @author randallco@users.sourceforge.net
 */
public class HexTableBuffer {
	private static final long BUFFER_DEFAULT_THRESHOLD = 64 * 1024;  // by default 64 kB

	private HexTable table;
	private String[] buffer = null;
	private File file = null;
	private int regionSize = 0;
	
	/**
	 * Constructor
	 * @param table
	 */
	public HexTableBuffer(HexTable table) {
		this(table, BUFFER_DEFAULT_THRESHOLD);
	}

	/**
	 * Constructor
	 * @param table
	 * @param treshold
	 */
	public HexTableBuffer(HexTable table, long treshold) {
		super();
		this.table = table;
	}

	/**
	 * Stores entire table to the buffer (memory or file, depends on table size)
	 */
	public void storeTableRegion() {
		storeTableRegion(0, 0);
	}

	/**
	 * Stores the table region from the specified position to the end to the buffer
	 * @param p table pointer
	 */
	public void storeTableRegion(HexTablePointer p) {
		storeTableRegion(p.getRowIndex(), p.getColumnIndex());
	}
	
	/**
	 * Stores the table region from the specified position to the end to the buffer
	 * @param rowIndexBegin
	 * @param columnIndexBegin
	 */
	public void storeTableRegion(int rowIndexBegin, int columnIndexBegin) {
		int lastItemIndex = table.getItemCount()-1;
		HexTableItem lastItem = table.getItem(lastItemIndex);
		storeTableRegion(rowIndexBegin, columnIndexBegin, lastItemIndex, lastItem.getItemSize()-1);
	}

	/**
	 * Stores the table region from the specified position to the end to the buffer
	 * @param p1 table pointer
	 * @param p2 table pointer
	 */
	public void storeTableRegion(HexTablePointer p1, HexTablePointer p2) {
		storeTableRegion(p1.getRowIndex(), p1.getColumnIndex(), p2.getRowIndex(), p2.getColumnIndex());
	}	
	/**
	 * Stores the specified table region to the buffer
	 * @param rowIndexBegin
	 * @param columnIndexBegin
	 * @param rowIndexEnd
	 * @param columnIndexEnd
	 * @return
	 */
	public boolean storeTableRegion(int rowIndexBegin, int columnIndexBegin, int rowIndexEnd, int columnIndexEnd) {
		EhepPlugin.log("storeTableRegion(): rowIndexBegin=" + rowIndexBegin + ", columnIndexBegin=" + columnIndexBegin + ", rowIndexEnd=" + rowIndexEnd + ", columnIndexEnd=" + columnIndexEnd);
		
		int dataToBackup = table.getTableSize(rowIndexBegin, columnIndexBegin, rowIndexEnd, columnIndexEnd);
		
		regionSize = dataToBackup;
		
		if (dataToBackup > BUFFER_DEFAULT_THRESHOLD) {
			//
			// Internal buffer is too small to backup the specified region - use temp file
			//
			return saveData(rowIndexBegin, columnIndexBegin, rowIndexEnd, columnIndexEnd);
		} // if
		else {
			//
			// Use internal buffer to backup specified region
			//
			return putData(rowIndexBegin, columnIndexBegin, rowIndexEnd, columnIndexEnd);
		} // else
	}

	/**
	 * Restores the table region from buffer into the table from specified position
	 * @param p table pointer
	 * @return result
	 */
	public boolean restoreTableRegion(HexTablePointer p) {
		if (file != null) {
			//
			// Get data from the backup file
			//
			return loadData(p);
		} // if
		else {
			//
			// Get data from the memory buffer
			//
			return getData(p);
		}
	}

	/**
	 * Puts the data into the memory buffer
	 * @param rowIndexBegin
	 * @param columnIndexBegin
	 * @param rowIndexEnd
	 * @param columnIndexEnd
	 * @return boolean true if the data was successfully written to the buffer, otherwise false
	 */
	private boolean putData(int rowIndexBegin, int columnIndexBegin, int rowIndexEnd, int columnIndexEnd) {
		//
		// Set "busy" cursor
		//
		Cursor waitCursor = new Cursor(table.getDisplay(), SWT.CURSOR_WAIT);
		table.getShell().setCursor(waitCursor);
		
		int bufferIndex = 0;
		HexTableItem item;
		int bufferSize = table.getTableSize(rowIndexBegin, columnIndexBegin, rowIndexEnd, columnIndexEnd);
		
		EhepPlugin.log("putData(): rowIndexBegin=" + rowIndexBegin + ", columnIndexBegin=" + columnIndexBegin + ", rowIndexEnd=" + rowIndexEnd + ", columnIndexEnd=" + columnIndexEnd + ", bufferSize=" + bufferSize);

		//
		// Allocate the buffer
		//
		buffer = new String[bufferSize];
		
		//
		// Load the current color settings
		//
		Color[][] colorSettings = Utils.getCurrentColorSettings();

		if (rowIndexBegin == rowIndexEnd) {
			item = table.getItem(rowIndexBegin);
			for (int i = columnIndexBegin; i <= columnIndexEnd; i++) {
				buffer[bufferIndex++] = item.getText(i+1) + item.getCellStatus(i+1, colorSettings);
			} // for
		} // if
		else {
			item = table.getItem(rowIndexBegin);
			for (int i = (columnIndexBegin+1); i <= EHEP.TABLE_NUM_DATA_COLUMNS; i++) {
				EhepPlugin.log("putData(1): buffer.size=" + buffer.length + ", bufferIndex=" + bufferIndex + ", row=" + rowIndexBegin + ", column=" + i + ", item=" + item);
				buffer[bufferIndex++] = item.getText(i) + item.getCellStatus(i, colorSettings);
			} // for
	
			for (int i = rowIndexBegin+1; i < rowIndexEnd; i++) {
				item = table.getItem(i);
				if (item != null) {
					for (int j = 1; j <= EHEP.TABLE_NUM_DATA_COLUMNS; j++) {
						EhepPlugin.log("putData(2): buffer.size=" + buffer.length + ", bufferIndex=" + bufferIndex + ", row=" + i + ", column=" + j + ", item=" + item);
						buffer[bufferIndex++] = item.getText(j) + item.getCellStatus(j, colorSettings);
					} // for
				} // if
				else {
					//
					// WTF!!! Error???
					//
					EhepPlugin.log("putData(2): buffer.size=" + buffer.length + ", bufferIndex=" + bufferIndex + ", row=" + i + ", item=null!!!!");
				}
			} // for
	
			item = table.getItem(rowIndexEnd);
			for (int i = 1; item != null && i <= (columnIndexEnd+1); i++) {
				EhepPlugin.log("putData(3): buffer.size=" + buffer.length + ", bufferIndex=" + bufferIndex + ", row=" + rowIndexEnd + ", column=" + i + ", item=" + item);
				buffer[bufferIndex++] = item.getText(i) + item.getCellStatus(i, colorSettings);
			} // for
		} // else

		//
		// Set the standard cursor
		//
		table.getShell().setCursor(null);
		waitCursor.dispose();

		colorSettings = null;

		return true;
	}

	/**
	 * Gets data from the memory buffer to the table from specified position
	 * @param p position where put data from the memory buffer
	 * @return boolean
	 */
	private boolean getData(HexTablePointer p) {
		if (buffer == null) return false;
		
		EhepPlugin.log("getData(): p=" + p.toString());
		
		//
		// Set "busy" cursor
		//
		Cursor waitCursor = new Cursor(table.getDisplay(), SWT.CURSOR_WAIT);
		table.getShell().setCursor(waitCursor);
		
		int row = p.getRowIndex();
		int column = p.getColumnIndex() + 1;
		HexTableItem item = table.getItem(row);

		//
		// Load the current color settings
		//
		Color[][] colorSettings = Utils.getCurrentColorSettings();

		for (int i = 0; i < buffer.length; i++) {
			if (column > EHEP.TABLE_NUM_DATA_COLUMNS) {
				item = table.getItem(++row);
				column = 1;
			} // if

			EhepPlugin.log("getData(" + i + "): row=" + row + ", column=" + column + ", item=" + item + ", buffer[" + i + "]=" + buffer[i] + ", buffer.length=" + buffer.length);
			
			//
			// Set the cell content
			//
			int index = buffer[i].indexOf(',');
			
			if (index >= 0) {
				//
				// Color attribute exists
				//
				item.setText(column, buffer[i].substring(0, index));
				int cellStatus = Integer.parseInt(buffer[i].substring(index+1));
				item.setBackground(column, colorSettings[cellStatus][0]);
				item.setForeground(column, colorSettings[cellStatus][1]);
			}
			else {
				//
				// No color attribute
				//
				item.setText(column, buffer[i]);
				item.setBackground(column, colorSettings[EHEP.CELL_STATUS_NORMAL][0]);
				item.setForeground(column, colorSettings[EHEP.CELL_STATUS_NORMAL][1]);
			} // else
			
			column++;
		} // for
		
		item.itemFiller();
		
		//
		// Set the standard cursor
		//
		table.getShell().setCursor(null);
		waitCursor.dispose();

		colorSettings = null;

		return true;
	}

	/**
	 * Saves the data to the temp file
	 * @param rowIndexBegin
	 * @param columnIndexBegin
	 * @param rowIndexEnd
	 * @param columnIndexEnd
	 * @return boolean true if the data was successfully written to the file, otherwise false
	 */
	private boolean saveData(int rowIndexBegin, int columnIndexBegin, int rowIndexEnd, int columnIndexEnd) {
		//
		// Set "busy" cursor
		//
		Cursor waitCursor = new Cursor(table.getDisplay(), SWT.CURSOR_WAIT);
		table.getShell().setCursor(waitCursor);

		//
		// Load the current color settings
		//
		Color[][] colorSettings = Utils.getCurrentColorSettings();

		//
		// Get the temp file descriptor
		//
		file = getTempFile();
		
		if (file.exists()) {
			//
			// Temp file already exists, delete it
			//
			file.delete();
		} // if
		
		BufferedWriter fileWriter = null;
		HexTableItem item;
		
		try {
			fileWriter = new BufferedWriter(new FileWriter(file, false));

			if (rowIndexBegin == rowIndexEnd) {
				item = table.getItem(rowIndexBegin);
				for (int i = columnIndexBegin; i <= columnIndexEnd; i++) {
					fileWriter.write("" + item.getText(i+1) + item.getCellStatus(i+1, colorSettings));
					fileWriter.newLine();
				} // for
			} // if
			else {
				item = table.getItem(rowIndexBegin);
				for (int i = columnIndexBegin; i < EHEP.TABLE_NUM_DATA_COLUMNS; i++) {
					fileWriter.write("" + item.getText(i+1) + item.getCellStatus(i+1, colorSettings));
					fileWriter.newLine();
				} // for
	
				for (int i = rowIndexBegin+1; i < rowIndexEnd; i++) {
					item = table.getItem(i);
					for (int j = 0; j < EHEP.TABLE_NUM_DATA_COLUMNS; j++) {
						fileWriter.write("" + item.getText(j+1) + item.getCellStatus(j+1, colorSettings));
						fileWriter.newLine();
					} // for
				} // for
	
				item = table.getItem(rowIndexEnd);
				for (int i = 0; i <= columnIndexEnd; i++) {
					fileWriter.write("" + item.getText(i+1) + item.getCellStatus(i+1, colorSettings));
					fileWriter.newLine();
				} // for
			} // else
		} // try
		
		catch (IOException e) {
			//
			// Oops, we got an exception!
			//
			MessageDialog.openError(table.getShell(), "IOException", "Error opening or writing to the file " + file.getAbsolutePath() + "!");
			colorSettings = null;
			return false;
		} // catch

		finally {
			//
			// Set the standard cursor
			//
			table.getShell().setCursor(null);
			waitCursor.dispose();

			if (fileWriter != null) {
				try {
					//
					// Try to close the stream
					//
					fileWriter.close();
				} // try
				
				catch (IOException e) {
					//
					// Oops, we got an exception!
					//
					MessageDialog.openError(table.getShell(), "IOException", "Error closing file " + file.getAbsolutePath() + "!");
					colorSettings = null;
					return false;
				} // catch
			} // if
		} // finally

		colorSettings = null;
		
		return true;
	}

	/**
	 * Loads the data from the temp file
	 * @param p Table pointer where to load the data
	 * @return boolean true if the data was successfully loaded from the file, otherwise false
	 */
	private boolean loadData(HexTablePointer p) {
		if (file == null) return false;
		
		//
		// Set "busy" cursor
		//
		Cursor waitCursor = new Cursor(table.getDisplay(), SWT.CURSOR_WAIT);
		table.getShell().setCursor(waitCursor);
		
		//
		// Load the current color settings
		//
		Color[][] colorSettings = Utils.getCurrentColorSettings();

		int row = p.getRowIndex();
		int column = p.getColumnIndex() + 1;
		HexTableItem item = table.getItem(row);

		BufferedReader fileReader = null;
		String s;
		
		try {
			fileReader = new BufferedReader(new FileReader(file));
			
			while ((s = fileReader.readLine()) != null) {
				if (column > EHEP.TABLE_NUM_DATA_COLUMNS) {
					item = table.getItem(++row);
					column = 1;
				} // if

				//
				// Set the cell content
				//
				int index = s.indexOf(',');
				
				if (index >= 0) {
					//
					// Color attribute exists
					//
					item.setText(column, s.substring(0, index));
					int cellStatus = Integer.parseInt(s.substring(index+1));
					item.setBackground(column, colorSettings[cellStatus][0]);
					item.setForeground(column, colorSettings[cellStatus][1]);
				}
				else {
					//
					// No color attribute
					//
					item.setText(column, s);
					item.setBackground(column, colorSettings[EHEP.CELL_STATUS_NORMAL][0]);
					item.setForeground(column, colorSettings[EHEP.CELL_STATUS_NORMAL][1]);
				} // else
				
				column++;
			} // while
		} // try
		
		catch (FileNotFoundException e) {
			//
			// Oops, we got an exception!
			//
			MessageDialog.openError(table.getShell(), "FileNotFoundException", "Error opening file " + file.getAbsolutePath() + "!");
			return false;
		} // catch

		catch (IOException e) {
			//
			// Oops, we got an exception!
			//
			MessageDialog.openError(table.getShell(), "IOException", "Error reading file " + file.getAbsolutePath() + "!");
			return false;
		} // catch

		finally {
			//
			// Set the standard cursor
			//
			table.getShell().setCursor(null);
			waitCursor.dispose();
			
			item.itemFiller();

			if (fileReader != null) {
				try {
					//
					// Try to close the stream
					//
					fileReader.close();
				} // try
				
				catch (IOException e) {
					//
					// Oops, we got an exception!
					//
					MessageDialog.openError(table.getShell(), "IOException", "Error closing file " + file.getAbsolutePath() + "!");
					return false;
				} // catch
				
				finally {
					//
					// Delete the temp file
					//
					file.delete();
				} // finally
			} // if
		} // finally

		return true;
	}

	/**
	 * Gets the temp. file descriptor
	 * @return the temp file descriptor
	 */
	private File getTempFile() {
		IPath path = EhepPlugin.getDefault().getStateLocation();
		String filename;
		Random random = new Random();
		
		do {
			filename = "tmp" + Math.abs(random.nextInt()) + ".bin";
			file = new File(path.toOSString() + File.separatorChar + filename);
		} while (file.exists());
		
		random = null;
		
		return file;
	}

	/**
	 * Gets the table region size
	 * @return the table region size
	 */
	public int getTableRegionSize() {
		return regionSize;
	}

	/**
	 * Disposes the object
	 */
	public void dispose() {
		buffer = null;
		file = null;
	}
}
